package com.starsray.dynamic.ds.core;

import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.toolkit.CryptoUtils;
import com.starsray.dynamic.ds.params.DatasourceProperties;
import com.starsray.dynamic.ds.util.SQLUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Map;
import java.util.Set;

/**
 * Ds Operator
 *
 * @author starsray
 * @since 2021-11-13
 */
public interface Datasource {

    /**
     * 列表数据源
     *
     * @return {@link Set<String> }
     */
    Set<String> listDatasource();


    /**
     * 添加数据源
     *
     * @param datasourceProperties datasource属性
     * @return {@link Set}<{@link String}>
     */
    Set<String> addDatasource(DatasourceProperties datasourceProperties);

    /**
     * 删除数据源
     *
     * @param poolName 名称
     * @return boolean
     */
    boolean removeDatasource(String poolName);


    /**
     * ds服务
     *
     * @author starsray
     * @since 2021-11-13
     */
    @Slf4j
    class Operator implements Datasource {

        /**
         * 查询数据源
         *
         * @return {@link Set<String> }
         */
        @Override
        public Set<String> listDatasource() {
            DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
            return ds.getDataSources().keySet();
        }


        /**
         * 添加数据源
         *
         * @param properties 属性
         * @return {@link Set}<{@link String}>
         */
        @Override
        public Set<String> addDatasource(DatasourceProperties properties) {
            DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
            Map<String, DataSource> dataSources = ds.getDataSources();
            if (dataSources.containsKey(properties.getPoolName())) {
                return dataSources.keySet();
            }

            DataSourceProperty dataSourceProperty = new DataSourceProperty();
            BeanUtils.copyProperties(properties, dataSourceProperty);

            // 加密密码
            try {
                properties.setPassword("ENC(" + CryptoUtils.encrypt(properties.getPassword()) + ")");
            } catch (Exception e) {
                e.printStackTrace();
                log.error("[dynamic ds] encrypt error! e: {}", e.getMessage());
            }

            try {
                Connection connection = dataSource.getConnection();
                PreparedStatement statement = connection.prepareStatement(SQLUtils.slaveDs(properties));
                statement.execute();
            } catch (SQLException e) {
                log.error("[dynamic ds] add datasource [{}] error! e: [{}]", properties.getPoolName(), e.getMessage());
                e.printStackTrace();
            }
            DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
            ds.addDataSource(properties.getPoolName(), dataSource);
            log.info("[dynamic ds] add datasource [{}] success!", properties.getPoolName());
            return dataSources.keySet();
        }


        /**
         * 删除数据源
         *
         * @param poolName 池名称
         * @return boolean
         */
        @Override
        public boolean removeDatasource(String poolName) {
            if ("master".equals(poolName)) {
                throw new RuntimeException("[dynamic ds] datasource master can't remove!");
            }
            DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
            if (!ds.getDataSources().containsKey(poolName)) {
                throw new RuntimeException("[dynamic ds] datasource " + poolName + " not exist!");
            }

            DataSource master = ds.getDataSource("master");
            try {
                Connection connection = master.getConnection();
                PreparedStatement statement = connection.prepareStatement(SQLUtils.removeDs(poolName));
                statement.execute();
            } catch (SQLException e) {
                e.printStackTrace();
                log.error("[dynamic ds] remove datasource [{}] error, e:{}", poolName, e.getMessage());
            }
            ds.removeDataSource(poolName);
            log.info("[dynamic ds] remove datasource [{}] success!", poolName);
            return true;
        }

        @Resource
        private DataSource dataSource;
        @Resource
        private DefaultDataSourceCreator dataSourceCreator;
    }
}
